import turtle as t
import random
import time

width = 600
height = 600
t.up()
screen = t.Screen()
screen.title('puzzle game')
screen.setup(width, height)
num_list = []
shape_list = []
text_list = []

# t.screen.bgcolor("orange")
level = int(screen.numinput("Hi", "Level:", 3, minval=3, maxval=5))
# print(level)
unit_size = 80
gap = 10
length = gap * (level + 1) + unit_size * level
init_x = length / 2
init_y = length / 2
select_num = None
des_num = None
running = True
step = 0

space_x = level - 1
space_y = level - 1


def inversion_num(num_list):
    inversions = 0
    for i in range(len(num_list)):
        if num_list[i] == 1:
            continue
        for j in range(i + 1, len(num_list)):
            if num_list[i] > num_list[j]:
                inversions += 1
    return inversions


def init_map():
    global num_list
    tmp_list = []
    size = level * level - 1
    while len(tmp_list) < size:
        num = int(random.random() * size) + 1
        if num not in tmp_list:
            tmp_list.append(num)

    # Record reverse order pairs
    inversions = inversion_num(tmp_list)
    if level % 2 != 0:
        # Make the number of reverse pairs even, and the game has a solution
        while inversions % 2 != 0:
            # Randomly swap two numbers to adjust the number of inversions
            i, j = random.sample(range(level * level - 1), 2)
            tmp_list[i], tmp_list[j] = tmp_list[j], tmp_list[i]
            inversions = inversion_num(tmp_list)

    if level % 2 == 0:
        # Make the number of reverse pairs even, and the game has a solution
        while (inversions + 1) % 2 != 0:
            # Randomly swap two numbers to adjust the number of inversions
            i, j = random.sample(range(level * level - 1), 2)
            tmp_list[i], tmp_list[j] = tmp_list[j], tmp_list[i]
            inversions = inversion_num(tmp_list)

    tmp_list.append(-1)

    for i in range(0, level):
        num_list.append(tmp_list[i * level:(i + 1) * level])

    # num_list = [[1,2,3],[4,5,6],[-1,7,8]]
    # print(num_list)

    for i in range(0, level):
        for j in range(0, level):
            if num_list[i][j] == -1:
                shape_list.append(None)
                text_list.append(None)
                continue
            t1 = t.Turtle()
            shape_list.append(t1)
            t2 = t.Turtle()
            text_list.append(t2)
            t1.ht()
            t2.ht()
            t1.speed(0)
            t2.speed(0)


def draw_square(t, x, y, length, color):
    d = 0
    t.up()
    t.goto(x, y)
    t.down()
    if color is not None:
        t.fillcolor(color)
        t.begin_fill()
    for k in range(4):  # 图形边数
        t.fd(length)  # 边长
        d = d - 90  # 旋转角度
        t.seth(d)  # 改变前进方向但不前进
    if color is not None:
        t.end_fill()


def draw_square2(t, x, y, length, color):
    t.up()
    t.goto(x, y)
    # t.down()
    if color is not None:
        t.fillcolor(color)
    t.shape('square')
    t.shapesize(3, 3)
    t.stamp()
    # t.ht()


def draw_text(t, x, y, text):
    t.up()
    t.goto(x, y)
    t.down()
    t.write(text, font=('Arial', 20, 'normal'))


def draw_finish_text(t):
    t.up()
    size = level - 1
    pos_x = -init_x + size * unit_size + gap * (size + 1)
    pos_y = init_y - (size * unit_size + gap * (size + 1))
    t.up()
    t.goto(pos_x - 1, pos_y - 10)
    t.down()
    t.write("Congraulations!", font=('Arial', 7, 'normal'))
    t.up()
    t.goto(pos_x - 1, pos_y - 25)
    t.down()
    t.write("You solved the puzzle", font=('Arial', 7, 'normal'))
    t.up()
    t.goto(pos_x - 1, pos_y - 40)
    t.down()
    t.write("in " + str(step) + " moves!", font=('Arial', 7, 'normal'))


def draw_map():
    global running
    t.ht()
    t.speed(0)
    draw_square(t, -init_x, init_y, length, None)
    status = check_finish()
    if status:
        running = False
    for i in range(0, level):
        for j in range(0, level):
            index = i * level + j
            t1 = shape_list[index]
            t2 = text_list[index]
            num_text = num_list[i][j]
            if num_list[i][j] == -1:
                continue
            color = "green"
            if status:
                t1.speed(0)
                t2.speed(0)
                color = "red"
            pos_x = -init_x + j * unit_size + gap * (j + 1)
            pos_y = init_y - (i * unit_size + gap * (i + 1))
            draw_square2(t1, pos_x + 40, pos_y - 40, unit_size, color)
            text_x = pos_x + (unit_size / 2 - 5)
            if num_text > 9:
                text_x = pos_x + (unit_size / 2 - 13)
            draw_text(t2, text_x, pos_y - (unit_size / 2 + gap), num_text)
    if status:
        draw_finish_text(t)


def check_finish():
    for i in range(0, level):
        for j in range(0, level):
            num = i * level + j + 1
            if i == (level - 1) and j == (level - 1):
                num = -1
            if num_list[i][j] != num:
                return False
    return True


def move(i, j):
    global space_x
    global space_y
    global running
    global step
    row = space_x
    col = space_y
    if (abs(row - i) == 1 and abs(col - j) == 0) or (abs(row - i) == 0 and abs(col - j) == 1):
        step += 1
        num_text = num_list[i][j]
        num_list[row][col] = num_list[i][j]
        num_list[i][j] = -1
        status = check_finish()
        if status:
            running = False
        text_list[i * level + j].clear()
        shape_list[i * level + j].clear()
        index = i * level + j
        t1 = shape_list[index]
        t2 = text_list[index]
        if i < row:
            # down
            t1.seth(270)
            t1.fd(unit_size + gap)
            t1.stamp()
        elif i > row:
            # up
            t1.seth(90)
            t1.fd(unit_size + gap)
            t1.stamp()

        elif j < col:
            # right
            t1.seth(0)
            t1.fd(unit_size + gap)
            t1.stamp()
        else:
            # left
            t1.seth(180)
            t1.fd(unit_size + gap)
            t1.stamp()
        pos_x = -init_x + col * unit_size + gap * (col + 1)
        pos_y = init_y - (row * unit_size + gap * (row + 1))
        text_x = pos_x + (unit_size / 2 - 5)
        if num_text > 9:
            text_x = pos_x + (unit_size / 2 - 13)
        t2.speed(10)
        draw_text(t2, text_x, pos_y - (unit_size / 2 + gap), num_text)
        running = True

        shape_list[row * level + col] = shape_list[i * level + j]
        text_list[row * level + col] = text_list[i * level + j]

        shape_list[i * level + j] = None
        text_list[i * level + j] = None
        space_x = i
        space_y = j
        if status:
            running = False
            draw_map()


def cursor_x_y(x, y):
    global running
    global space_x
    global space_y
    if not running:
        return
    for i in range(0, level):
        for j in range(0, level):
            pos_x = -init_x + j * unit_size + gap * (j + 1)
            pos_y = init_y - (i * unit_size + gap * (i + 1))
            # print(x, y, pos_x, pos_y)
            if pos_x <= x <= (pos_x + unit_size) and (pos_y - unit_size) <= y <= pos_y:
                if (abs(space_x - i) == 1 and abs(space_y - j) == 0) or (
                        abs(space_x - i) == 0 and abs(space_y - j) == 1):
                    running = False
                    move(i, j)


init_map()
draw_map()
# print(num_list)                            

screen.onclick(cursor_x_y)
screen.mainloop()
